#ifndef _POSIX_PTHREAD_SEMANTICS
#define _POSIX_PTHREAD_SEMANTICS
#endif
#include "sthread.h"
#include <assert.h>
#include <pthread.h>
#include <sched.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void smutex_init(smutex_t *mutex)
{
  if(pthread_mutex_init(mutex, NULL)){
      perror("pthread_mutex_init failed");
      exit(-1);
  }    
}

void smutex_destroy(smutex_t *mutex)
{
  if(pthread_mutex_destroy(mutex)){
      perror("pthread_mutex_destroy failed");
      exit(-1);
  }    
}

void smutex_lock(smutex_t *mutex)
{
  if(pthread_mutex_lock(mutex)){
    perror("pthread_mutex_lock failed");
    exit(-1);
  }    
}

void smutex_unlock(smutex_t *mutex)
{
  if(pthread_mutex_unlock(mutex)){
    perror("pthread_mutex_unlock failed");
    exit(-1);
  }    
}



void scond_init(scond_t *cond, smutex_t *mutex)
{
  assert(cond);
  assert(mutex);
  cond->mutex = mutex;
  if(pthread_cond_init(&(cond->cond), NULL)){
      perror("pthread_cond_init failed");
      exit(-1);
  }
}

void scond_destroy(scond_t *cond)
{
  cond->mutex = NULL;
  if(pthread_cond_destroy(&(cond->cond))){
      perror("pthread_cond_destroy failed");
      exit(-1);
  }
}

void scond_signal(scond_t *cond, smutex_t * mutex)
{
  assert(mutex == cond->mutex);

  //
  // assert(mutex is held by this thread);
  //

  if(pthread_cond_signal(&(cond->cond))){
    perror("pthread_cond_signal failed");
    exit(-1);
  }
}

void scond_broadcast(scond_t *cond, smutex_t * mutex)
{

  assert(mutex == cond->mutex);

  //
  // assert(mutex is held by this thread);
  //
  if(pthread_cond_broadcast(&(cond->cond))){
    perror("pthread_cond_broadcast failed");
    exit(-1);
  }
}

void scond_wait(scond_t *cond, smutex_t *mutex)
{
  assert(mutex == cond->mutex);

  //
  // assert(mutex is held by this thread);
  //

  if(pthread_cond_wait(&(cond->cond), mutex)){
    perror("pthread_cond_wait failed");
    exit(-1);
  }
}



void sthread_create(sthread_t *thread,
		    void *(*start_routine)(void*), 
		    void *argToStartRoutine)
{
  if(pthread_create(thread, NULL, start_routine, argToStartRoutine)){
      perror("pthread_create failed");
      exit(-1);
  }
}

void sthread_exit(void)
{
  pthread_exit(NULL);
}





void sthread_sleep(unsigned int seconds, unsigned int nanoseconds)
{
  struct timespec rqt;
  assert(nanoseconds < 1000000000);
  rqt.tv_sec = seconds;
  rqt.tv_nsec = nanoseconds;
  if(nanosleep(&rqt, NULL) != 0){
    perror("sleep failed. Woke up early");
    exit(-1);
  }
}

void sthread_yield()
{
  sched_yield();
}




/*
 * Not clear if random() is thread safe. These days it probably is, but
 * better safe than sorry. 
 */

pthread_mutex_t  sulock = PTHREAD_MUTEX_INITIALIZER;
long 
sutil_random(){

  long ret;

  if(pthread_mutex_lock(&sulock)){
    perror("random lock");
    exit(-1);
  }
  
  ret = random();
    
  if(pthread_mutex_unlock(&sulock)){
    perror("random unlock");
    exit(-1);
  }

  return ret;

}

void
sutil_srandom(unsigned seed)
{
  if(pthread_mutex_lock(&sulock)){
    perror("random lock");
    exit(-1);
  }
  
  srandom(seed);
    
  if(pthread_mutex_unlock(&sulock)){
    perror("random unlock");
    exit(-1);
  }

  return;
}

